#include "IFUException.h"
#include "FITSException.h"

#include "Image.h"
#include "FITSImage.h"

#include <string>

using namespace std;

extern "C" {
  #include <fitsio.h>
}

FITSImage::FITSImage() : Image() {
}


FITSImage::FITSImage(const string &filename) : Image(filename) {
  read(this->filename);
}

FITSImage::~FITSImage() {

  for (int i = 0; i < nRows; i++)
    delete []pixels[i];

  delete []pixels;

}

void FITSImage::close() {

  int status = 0;
  
  if (fits_close_file(this->fp, &status))
    throw FITSException("FITSImage: error closing file: " + this->filename, status);

}

void FITSImage::create(const string &filename, float **pixels, int nRows, int nCols) {
  setFilename(filename);
  write(filename, pixels, nRows, nCols);
}

void FITSImage::save(const string &filename, int withHeaders) {

  if (this->fp != NULL) {
    write(filename, this->pixels, this->nRows, this->nCols);
    
    if (withHeaders){
      // escreve cabecalhos
    }
    
  } else {
    
    throw IFUException("You must open first.", "FITSImage");

  }

}

void FITSImage::read(const string &filename) {

  cout << "-> FitsImage: " << filename << endl;

   int status = 0;
   int tipo;
   int bitpix;
   int axes;
   long naxes[2];
   int x;
   long fpixel[2];
   
   // open
   if (fits_open_file(&this->fp, filename.c_str(), READONLY, &status))
     throw FITSException("FITSImage: error opening file: " + filename, status);
  
   // checa o tipo da extensao, somente IMAGE_HDU (0) eh permitida
   if (fits_get_hdu_type(this->fp, &tipo, &status))
     throw FITSException("FITSImage: error in cfitsio.", status);
   
   if (tipo != IMAGE_HDU)
     throw FITSException("FITSImage: only image are allowed: " + filename, status);
   
   // checa quantidade de dimensoes da IMAGE
   if (fits_get_img_dim(this->fp, &axes, &status))
     throw FITSException("FITSImage: error in cfitsio.", status);
   if (axes != 2)
     throw FITSException("FITSImage: The image must be 2D: " + filename, status);
   
   /*
    * pega os parametros: BITPIX, NAXIS1, NAXIS2
    * nota: NAXIS1(axes[0]) = colunas, NAXIS2(axes[1]) = linhas
    */
   if (fits_get_img_param(this->fp, 2, &bitpix, &axes, naxes, &status))
     throw FITSException("FITSImage: error in cfitsio.", status);

   this->nCols = naxes[0];
   this->nRows = naxes[1];

   // memory allocation
   pixels = new float*[nRows];
   for (x = 0; x < nRows; x++)
      pixels[x] = new float[nCols];
   
   // le o fits, linha por linha
   fpixel[0] = 1;
   for (fpixel[1] = 1; fpixel[1] <= nRows; fpixel[1]++){
      if (fits_read_pix(this->fp, TFLOAT, fpixel, naxes[0], 0, 
			pixels[(fpixel[1]-1)], 0, &status))
	throw FITSException("FITSImage: error in cfitsio.", status);
   }

}

void FITSImage::write(const string &filename, float **pixels, int nRows, int nCols) {

  float *pix;
  int status = 0;
  long fpixel = 1;
  long naxis = 2;
  long naxes[2] = { nCols, nRows };
  long nelements = naxes[0] * naxes[1];
  long i, x, y;
    
  pix = new float[nelements];

  // transforma a matrix em um vetor (cfitsio rules!)
  for (x = y = i = 0; i < nelements; i++, y++) {
    if (y == nCols) {
      y = 0;
      x++;
    }
    pix[i] = pixels[x][y];
  }

  // fecha o file descriptor do arquivo ja aberto
  this->close();
    
  if (fits_create_file(&this->fp, filename.c_str(), &status))
    throw FITSException("FITSImage: error in cfitsio.", status);

  if (fits_create_img(this->fp, FLOAT_IMG, naxis, naxes, &status))
    throw FITSException("FITSImage: error creating image.", status);


  if (fits_write_img(this->fp, TFLOAT, fpixel, nelements, pix, &status))
    throw FITSException("FITSImage: error writing image.", status);

  // free's
  delete []pix;

}
